﻿
//////////////////////////////////////////////////////////////////////////////////

#define XOPUS_CTXT(c, t, r) \
    ((((x_uint32_t)((c) - 1)) << 31) | ((((x_uint32_t)(t)) & 0x7F) << 24) | (((x_uint32_t)(r)) & 0x00FFFFFF))
#define XOPUS_CC(ctxt)   (((x_uint32_t)(ctxt) >> 31) + 1)
#define XOPUS_CT(ctxt)   (((x_uint32_t)(ctxt) >> 24) & 0x7F)
#define XOPUS_CR(ctxt)   (((x_uint32_t)(ctxt) & 0x00FFFFFF))

/**********************************************************/
/**
 * @brief 写入 OPUS 文件头信息（成功，返回 1；失败，返回 0）。
 */
static x_int32_t opus_whead(FILE * fwstream, x_uint32_t xut_ctxt)
{
    x_char_t xbuf_tag[4] = { 'O', 'P', 'H', 'D' };
    x_byte_t xbuf_len[4] =
    {
        (x_byte_t)((xut_ctxt & 0x000000FF)      ),
        (x_byte_t)((xut_ctxt & 0x0000FF00) >>  8),
        (x_byte_t)((xut_ctxt & 0x00FF0000) >> 16),
        (x_byte_t)((xut_ctxt & 0xFF000000) >> 24)
    };

    if (X_NULL == fwstream)
    {
        return 0;
    }

    if (4 != (x_int32_t)fwrite(xbuf_tag, sizeof(x_char_t), 4, fwstream))
    {
        return 0;
    }

    if (4 != (x_int32_t)fwrite(xbuf_len, sizeof(x_byte_t), 4, fwstream))
    {
        return 0;
    }

    return 1;
}

/**********************************************************/
/**
 * @brief 读取 OPUS 文件头信息（成功，返回 1；失败，返回 0）。
 */
static x_int32_t opus_rhead(FILE * frstream, x_uint32_t * xut_ctxt)
{
    x_char_t xbuf_tag[4] = { 0 };
    x_byte_t xbuf_len[4] = { 0 };
    x_uint32_t xut_size = 0;

    if ((X_NULL == frstream) || (X_NULL == xut_ctxt))
    {
        return -1;
    }

    //======================================

    if (4 != fread(xbuf_tag, sizeof(x_char_t), 4, frstream))
    {
        return 0;
    }

    if (('O' != xbuf_tag[0]) || ('P' != xbuf_tag[1]) ||
        ('H' != xbuf_tag[2]) || ('D' != xbuf_tag[3]))
    {
        return 0;
    }

    //======================================

    if (4 != fread(xbuf_len, sizeof(x_byte_t), 4, frstream))
    {
        return 0;
    }

    *xut_ctxt = (((x_uint32_t)xbuf_len[0])      ) |
                (((x_uint32_t)xbuf_len[1]) <<  8) |
                (((x_uint32_t)xbuf_len[2]) << 16) |
                (((x_uint32_t)xbuf_len[3]) << 24);

    //======================================
    
    return 1;
}

/**********************************************************/
/**
 * @brief 将编码好的 OPUS 音频帧写入文件。
 * 
 * @param [in ] fwstream  : 目标文件对象。
 * @param [in ] xbuf_opus : OPUS 音频帧缓存。
 * @param [in ] xut_fsize : OPUS 音频帧缓存中的有效字节数。
 * 
 * @return x_int32_t 
 *         - 返回 -1，操作失败；
 *         - 返回 写入帧的字节数。
 */
static x_int32_t opus_wframe(
    FILE * fwstream, const x_void_t * xbuf_opus, x_uint32_t xut_fsize)
{
    x_char_t xbuf_tag[4] = { 'O', 'P', 'U', 'S' };
    x_byte_t xbuf_len[4] =
    {
        (x_byte_t)((xut_fsize & 0x000000FF)      ),
        (x_byte_t)((xut_fsize & 0x0000FF00) >>  8),
        (x_byte_t)((xut_fsize & 0x00FF0000) >> 16),
        (x_byte_t)((xut_fsize & 0xFF000000) >> 24)
    };

    if ((X_NULL == fwstream) || (X_NULL == xbuf_opus) || (xut_fsize <= 0))
    {
        return -1;
    }

    if (4 != (x_int32_t)fwrite(xbuf_tag, sizeof(x_char_t), 4, fwstream))
    {
        return -1;
    }

    if (4 != (x_int32_t)fwrite(xbuf_len, sizeof(x_byte_t), 4, fwstream))
    {
        return -1;
    }

    return (x_int32_t)fwrite(xbuf_opus, sizeof(x_byte_t), xut_fsize, fwstream);
}

/**********************************************************/
/**
 * @brief 从文件中读取编码好的 OPUS 音频帧。
 *  
 * @param [in    ] frstream  : 目标文件对象。
 * @param [out   ] xbuf_opus : 接收 OPUS 音频帧的缓存。
 * @param [in,out] xut_fsize : 入参，xbuf_opus 缓存容量；
 *                             回参，所读取的 OPUS 音频帧大小。
 * 
 * @return x_int32_t 
 *         - 返回 -1，操作失败；
 *         - 返回 读取到的字节数。
 */
static x_int32_t opus_rframe(
    FILE * frstream, x_void_t * xbuf_opus, x_uint32_t & xut_fsize)
{
    x_char_t xbuf_tag[4] = { 0 };
    x_byte_t xbuf_len[4] = { 0 };
    x_uint32_t xut_size = 0;

    if ((X_NULL == frstream) || (X_NULL == xbuf_opus) || (xut_fsize <= 0))
    {
        return -1;
    }

    //======================================

    if (4 != fread(xbuf_tag, sizeof(x_char_t), 4, frstream))
    {
        return -1;
    }

    if (('O' != xbuf_tag[0]) || ('P' != xbuf_tag[1]) ||
        ('U' != xbuf_tag[2]) || ('S' != xbuf_tag[3]))
    {
        return -1;
    }

    //======================================

    if (4 != fread(xbuf_len, sizeof(x_byte_t), 4, frstream))
    {
        return -1;
    }

    xut_size = (((x_uint32_t)xbuf_len[0])      ) |
               (((x_uint32_t)xbuf_len[1]) <<  8) |
               (((x_uint32_t)xbuf_len[2]) << 16) |
               (((x_uint32_t)xbuf_len[3]) << 24);
    if (xut_fsize < xut_size)
    {
        xut_fsize = xut_size;
        fseek(frstream, -8, SEEK_CUR);
        return 0;
    }

    //======================================

    xut_fsize = xut_size;

    return (x_int32_t)fread(xbuf_opus, sizeof(x_byte_t), xut_size, frstream);
}

//////////////////////////////////////////////////////////////////////////////////
